package com.yungu.swift.monitor.config;

import com.yungu.swift.monitor.license.IpAndMac;
import com.yungu.swift.monitor.model.Metric;
import com.yungu.swift.utils.MapUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;

import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 监控任务
 *
 * @author : cuixiuyin
 */
@Slf4j
public class MonitorTask implements Runnable {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 阻塞队列，存储两次机器信息
     */
    public static BlockingQueue<Map<String, Metric>> QUEUE = new LinkedBlockingQueue<>(2);

    private static final DecimalFormat DF = new DecimalFormat("#.00");


    @Override
    public void run() {
        // 1. 如果队列数量不足 2，说明是首次检测或者刚监测结束，直接返回
        if (QUEUE.size() < 2) {
            return;
        }
        // 2. 取出两次的监控信息，删除前一次的监控信息，保留第二次的监控信息
        Map<String, Metric> poll = QUEUE.poll();
        Map<String, Metric> peek = QUEUE.peek();
        if (CollectionUtils.isEmpty(poll.values()) || CollectionUtils.isEmpty(peek.values())) {
            return;
        }
        // 3. 遍历每个应用的监控信息，应用报警
        Iterator<Map.Entry<String, Metric>> pollIt = poll.entrySet().iterator();
        while (pollIt.hasNext()) {
            Map.Entry<String, Metric> next = pollIt.next();
            Metric previous = next.getValue();
            Metric now = peek.get(next.getKey());
            // 应用报警规则
            // 3.1 应用可用内存小于 20%
            if (Double.valueOf(now.getFreeMemory().toString()) / Double.valueOf(now.getMemory().toString()) < 0.2) {
                sendSms(next.getKey(), "应用可用内存小于 20% 报警，请及时处理", now);
            }
            // 3.2 应用内存突然飙升，两次差异大于 50%
            long total = previous.getMemory() + now.getMemory();
            if (previous.getFreeMemory() - now.getFreeMemory() > total >> 2) {
                sendSms(next.getKey(), "应用内存突然飙升，可能存在异常，请及时查看", now);
            }
        }
        // 4. 取每个应用采集到的平均值，机器报警
        Collection<Metric> metrics = peek.values();
        List<Metric> metricList = new ArrayList<>(metrics);
        Metric metric = metricList.get(new Random().nextInt(metricList.size()));
        // 机器报警规则
        // 4.1 可用磁盘小于 10G
        double aveFreeDisk = metrics.stream().mapToLong(Metric::getFreeDisk).average().getAsDouble();
        if (aveFreeDisk < 10 << 20) {
            sendSms(null, "可用磁盘小于10G，请及时处理", metric);
        }
        double aveIdle = metrics.stream().mapToDouble(Metric::getIdle).average().getAsDouble();
        // 4.2 CPU 平均使用率高余 80%
        if (aveIdle > 0.8) {
            sendSms(null, "CPU 使用率过高，可能存在异常，请及时查看", metric);
        }
        // 4.3 机器可用内存小于 10%
        double avgMachineFreeMem = metrics.stream().mapToLong(Metric::getMachineFreeMemory).average().getAsDouble();
        if (avgMachineFreeMem / Double.valueOf(metric.getMachineMemory().toString()) < 0.1) {
            sendSms(null, "机器可用内存小于 10% 报警，请及时处理", metric);
        }
    }


    private void sendSms(String key, String description, Metric metric) {
        StringBuilder content = new StringBuilder();
        content.append("<font color=\"warning\">").append(key == null ? "【机器报警】" : "【应用报警】").append("</font>");
        content.append(description).append("\n");
        try {
            content.append("> 机器IP：").append(IpAndMac.getInternetIp()).append("\n");
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        if (key != null) {
            content.append("> 应用名称：").append(key).append("\n");
            content.append("> 应用状态：").append(metric.getStatus()).append("\n");
            content.append("> 应用可用内存/总内存：")
                    .append(DF.format(metric.getFreeMemory() / 1024.0)).append(" / ")
                    .append(DF.format(metric.getMemory() / 1024.0)).append(" MB \n");
            content.append("> 应用堆已用内存/初始化内存：")
                    .append(DF.format(metric.getHeapUsedMemory() / 1024.0)).append(" / ")
                    .append(DF.format(metric.getHeapMemory() / 1024.0)).append(" MB \n ");
            content.append("> 应用栈已用内存/最大内存：")
                    .append(DF.format(metric.getNonHeapUsedMemory() / 1024.0)).append(" / ")
                    .append(DF.format(metric.getNonHeapMemory() / 1024.0)).append(" MB \n ");
            content.append("> JVM 新生代 GC回收次数/时长：")
                    .append(metric.getYoungGcCount()).append(" / ").append(metric.getYoungGcTime()).append(" ms \n  ");
            content.append("> JVM 老年代 GC回收次数/时长：")
                    .append(metric.getOldGcCount()).append(" / ").append(metric.getOldGcTime()).append(" ms \n  ");
            content.append("> 应用当前线程数量：").append(metric.getCurrentThreads()).append(" \n ");

        }
        content.append("> 机器空闲内存/总内存：")
                .append(DF.format(metric.getMachineFreeMemory() / (1024 * 1024 * 1024.0))).append(" / ")
                .append(DF.format(metric.getMachineMemory() / (1024 * 1024 * 1024.0))).append(" GB \n");
        content.append("> 机器空闲磁盘容量/总容量：")
                .append(DF.format(metric.getFreeDisk() / (1024 * 1024 * 1024.0))).append(" / ")
                .append(DF.format(metric.getDisk() * 1.0 / (1024 * 1024 * 1024.0))).append(" GB \n");
        content.append("> 机器CPU使用率：").append(Math.round(metric.getIdle() * 100)).append(" % \n");

        Map<String, Object> paramMap = new HashMap<>(4);
        paramMap.put("msgtype", "markdown");
        paramMap.put("markdown", MapUtils.build("content", content.toString()));
        restTemplate.postForObject("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=08b7fd13-453b-4997-9b90-e5286dffe3f3", paramMap, String.class);
    }
}
